﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using Microsoft.Win32;
using System.Xml.Linq;

namespace PSE.Framework.Versions.Common.Configuration
{
    public class VersionsConfiguration
    {
        private static object _lock = new object();
        private static VersionsConfiguration _versionsConfiguration;
        private VersionsConfiguration() { }
        public static VersionsConfiguration Instance
        {
            get
            {
                lock (_lock)
                {
                    if (_versionsConfiguration == null)
                        _versionsConfiguration = new VersionsConfiguration();

                    return _versionsConfiguration;
                }
            }
        }

        public List<VersionServer> GetAllServers(string product, int majorVersion)
        {
            string installLocation = GetInstallLocation(product, majorVersion);
            string generalSettingsPath = System.IO.Path.Combine(installLocation, "GeneralSettings.config");
            string settingsFolder = System.IO.Path.Combine(installLocation, @"Settings\Endpoints");

            List<VersionServer> serversFromGeneralSettings = GetServersFromGeneralSettings(generalSettingsPath);
            List<VersionServer> serversFromSettingsFolder = GetServersFromSettingsFolder(settingsFolder);

            List<VersionServer> versionServers = new List<VersionServer>();

            if (serversFromGeneralSettings != null && serversFromGeneralSettings.Count > 0)
                versionServers.AddRange(serversFromGeneralSettings);
            if (serversFromSettingsFolder != null && serversFromSettingsFolder.Count > 0)
                versionServers.AddRange(serversFromSettingsFolder);

            return versionServers.Distinct(new DistinctVersionServerComparer()).ToList();
        }

        private string GetInstallLocation(string product, int majorVersion)
        {
            string productVersion = Util.Instance.CombineSystemNameAndMajorVersion(product, majorVersion);
            string path = string.Format(Constants.REGISTRY_SYSTEM_ROOT_PATH, productVersion);
            string installLocation = RegistryUtil.Instance.GetValue(path, Constants.REGISTRY_KEY_INSTALL_LOCATION);

            return System.IO.Path.Combine(installLocation, product);
        }

        private List<VersionServer> GetServersFromGeneralSettings(string generalSettingsPath)
        {
            if (!System.IO.File.Exists(generalSettingsPath))
                return null;

            List<VersionServer> versionServers = ExtractsVersionServerFromGeneralSettingsFile(generalSettingsPath);

            return versionServers;
        }

        private List<VersionServer> GetServersFromSettingsFolder(string settingsFolderPath)
        {
            if(!System.IO.Directory.Exists(settingsFolderPath))
                return null;
             
            List<string> allFiles = new List<string>();

            string[] directories = System.IO.Directory.GetDirectories(settingsFolderPath);
            foreach (var directory in directories)
            {
                string[] files = System.IO.Directory.GetFiles(directory, "*.config");
                if (files.Length > 0) 
                    allFiles.Add(files[0]);
            }

            List<VersionServer> versionServers = new List<VersionServer>();
            foreach (var file in allFiles)
            {
                var version = ExtractsVersionServerFromEndPointFile(file);
                if (version != null)
                    versionServers.Add(version);
            }

            return versionServers;
        }

        private List<VersionServer> ExtractsVersionServerFromGeneralSettingsFile(string generalSettingsFile)
        {
            XDocument generalSettingsXml = XDocument.Load(generalSettingsFile);

            VersionServer servicesConfiguration = GetServicesConfigurationServer(generalSettingsXml);
            VersionServer inteligenceManagerConfiguration = GetInteligenceManagerConfigurationServer(generalSettingsXml);
            List<VersionServer> logPackagerConfiguration = GetLogPackagerConfigurationServer(generalSettingsXml);
            VersionServer updateConfiguration = GetUpdateConfigurationServer(generalSettingsXml);

            List<VersionServer> versions = new List<VersionServer>();
            if(servicesConfiguration != null) versions.Add(servicesConfiguration);
            if (inteligenceManagerConfiguration != null) versions.Add(inteligenceManagerConfiguration);
            if (updateConfiguration != null) versions.Add(updateConfiguration);
            if (logPackagerConfiguration != null && logPackagerConfiguration.Count > 0) versions.AddRange(logPackagerConfiguration);

            return versions;
        }

        private VersionServer GetVersionServerFromXml(XDocument generalSettingsXml, string tagName, string attributeName)
        {
            var tags = generalSettingsXml.Descendants(tagName);
            foreach (var tag in tags)
            {
                var address = tag.Attribute(attributeName);
                VersionServer versionServer = CreateVersionServerByUrl(address.Value);
                if (versionServer != null)
                    return versionServer;
            }
            return null;            
        }

        private VersionServer GetServicesConfigurationServer(XDocument generalSettingsXml)
        {
            return GetVersionServerFromXml(generalSettingsXml, "servicesConfiguration", "issuerAddress");
        }

        private VersionServer GetInteligenceManagerConfigurationServer(XDocument generalSettingsXml)
        {
            return GetVersionServerFromXml(generalSettingsXml, "inteligenceManagerConfiguration", "inteligenceManagerAddress");
        }

        private List<VersionServer> GetLogPackagerConfigurationServer(XDocument generalSettingsXml)
        {
            List<VersionServer> versionServers = new List<VersionServer>();

            var logPackagerConfigurations = generalSettingsXml.Descendants("logPackagerConfiguration");
            foreach (var logPackagerConfiguration in logPackagerConfigurations)
            {
                var sources = logPackagerConfiguration.Descendants("source");
                foreach (var source in sources)
                {
                    var address = source.Attribute("serviceAddress");
                    VersionServer versionServer = CreateVersionServerByUrl(address.Value);
                    if (versionServer != null)
                        versionServers.Add(versionServer);
                }
            }
            return versionServers; 
        }

        private VersionServer GetUpdateConfigurationServer(XDocument generalSettingsXml)
        {
            return GetVersionServerFromXml(generalSettingsXml, "updateConfiguration", "defaultUpdateServerUrl");
        }

        private VersionServer ExtractsVersionServerFromEndPointFile(string endpointFile)
        {
            XDocument document = XDocument.Load(endpointFile);
            var endpoints = document.Descendants("endpoint");
            foreach (var endpoint in endpoints)
            {
                var address = endpoint.Attribute("address");
                VersionServer versionServer = CreateVersionServerByUrl(address.Value);
                if (versionServer != null)
                    return versionServer;
            }
            return null;
        }

        private VersionServer CreateVersionServerByUrl(string url)
        {
            Uri uri = new Uri(url);
            //if (uri.Port > 0)
            //{
            //    return new VersionServer
            //    {
            //        ServerName = uri.Host,
            //        Port = uri.Port
            //    };
            //}
            //return null;

            return new VersionServer
            {
                ServerName = uri.Host,
                Port = Constants.SERVER_DEFAULT_PORT
            };
        }

        private class DistinctVersionServerComparer : IEqualityComparer<VersionServer>
        {
            public bool Equals(VersionServer x, VersionServer y)
            {
                return x.Equals(y);
            }

            public int GetHashCode(VersionServer obj)
            {
                return obj.Port.GetHashCode() ^ obj.ServerName.GetHashCode();
            }
        }
    }
}